package jalgebrava.group;

import jalgebrava.util.collections.ImmutableIterable;
import jalgebrava.util.collections.ImmutableIterator;
import jalgebrava.util.collections.IterableUtil;

/** 
 * The contract is that once you have an GroupElement<G> e, it is guaranteed
 * to have elt.group.contains(elt.x).
 */
public final class GroupElement<G> implements ElementsIterable<G> {
	public final G g;
	private final Group<G> group;
	
	public final Group<G> getGroup() {
		return group;
	}
	private GroupElement(Group<G> group, G g2) {
		this.group = group;
		this.g = g2;
	}

	@Override
	public ImmutableIterator<G> iterator() {
		return IterableUtil.singletonIterator(g);
	}
	

	@Override
	public ImmutableIterable<GroupElement<G>> elements() {
		return IterableUtil.singletonIterable(this);
	}

	/**
	 * Will check membership.
	 */
	public static <G> GroupElement<G> mk(Group<G> group, G g) {
		// TODO caching ?
		if (group.contains(g)) {
			return new GroupElement<G>(group,g);
		}
		throw new RuntimeException(g + " is not an element of " + group);
	}

	/** Will not check membership, except via assertion. */
	static <G> GroupElement<G> mkAssert(Group<G> group, G g) {
		assert(group.contains(g));
		return new GroupElement<G>(group,g);
		
	}

	
	public final GroupElement<G> inv() {
		return group.invElement(this);
	}
	
	public final GroupElement<G> mul(GroupElement<G> x) {
		if (this.group != x.group) {
			throw new RuntimeException("Incompatible groups " + this.group + " and " + x.group);
		}
		return group.mulElement(this, x);
	}
	
	// Object overrides	
	@SuppressWarnings("unchecked")
	@Override
	public final boolean equals(Object obj) {
		if (obj == null || !(obj instanceof GroupElement)) {
			return false;
		}
		
		GroupElement<G> objG = (GroupElement<G>) obj;
		
		G objAsG = objG.g;
		
		return group == objG.group && group.equals(this.g, objAsG);
		
	}
	@Override
	public final int hashCode() {
		return group.hashCode(this.g);
	}
	@Override
	public final String toString() {
		return group.toString(this.g);
	}

	public final String toShortString() {
		return group.toShortString(this.g);
	}
	
	public final int order() {
		return group.order(g);
	}
}
